
`include "defines.v"
//----------------------------------------------------------------
//Module Name : axi_rw.v
//Description of module:
// use axi4 to read or write data from ram/rom
//----------------------------------------------------------------
//Designer:	Tang Pengyu
//Date: 2021/09/08  
//----------------------------------------------------------------

// Burst types
`define AXI_BURST_TYPE_FIXED                                2'b00
`define AXI_BURST_TYPE_INCR                                 2'b01
`define AXI_BURST_TYPE_WRAP                                 2'b10
// Access permissions  for prot
`define AXI_PROT_UNPRIVILEGED_ACCESS                        3'b000
`define AXI_PROT_PRIVILEGED_ACCESS                          3'b001
`define AXI_PROT_SECURE_ACCESS                              3'b000
`define AXI_PROT_NON_SECURE_ACCESS                          3'b010
`define AXI_PROT_DATA_ACCESS                                3'b000
`define AXI_PROT_INSTRUCTION_ACCESS                         3'b100
// Memory types (AR)
`define AXI_ARCACHE_DEVICE_NON_BUFFERABLE                   4'b0000
`define AXI_ARCACHE_DEVICE_BUFFERABLE                       4'b0001
`define AXI_ARCACHE_NORMAL_NON_CACHEABLE_NON_BUFFERABLE     4'b0010
`define AXI_ARCACHE_NORMAL_NON_CACHEABLE_BUFFERABLE         4'b0011
`define AXI_ARCACHE_WRITE_THROUGH_NO_ALLOCATE               4'b1010
`define AXI_ARCACHE_WRITE_THROUGH_READ_ALLOCATE             4'b1110
`define AXI_ARCACHE_WRITE_THROUGH_WRITE_ALLOCATE            4'b1010
`define AXI_ARCACHE_WRITE_THROUGH_READ_AND_WRITE_ALLOCATE   4'b1110
`define AXI_ARCACHE_WRITE_BACK_NO_ALLOCATE                  4'b1011
`define AXI_ARCACHE_WRITE_BACK_READ_ALLOCATE                4'b1111
`define AXI_ARCACHE_WRITE_BACK_WRITE_ALLOCATE               4'b1011
`define AXI_ARCACHE_WRITE_BACK_READ_AND_WRITE_ALLOCATE      4'b1111
// Memory types (AW)
`define AXI_AWCACHE_DEVICE_NON_BUFFERABLE                   4'b0000
`define AXI_AWCACHE_DEVICE_BUFFERABLE                       4'b0001
`define AXI_AWCACHE_NORMAL_NON_CACHEABLE_NON_BUFFERABLE     4'b0010
`define AXI_AWCACHE_NORMAL_NON_CACHEABLE_BUFFERABLE         4'b0011
`define AXI_AWCACHE_WRITE_THROUGH_NO_ALLOCATE               4'b0110
`define AXI_AWCACHE_WRITE_THROUGH_READ_ALLOCATE             4'b0110
`define AXI_AWCACHE_WRITE_THROUGH_WRITE_ALLOCATE            4'b1110
`define AXI_AWCACHE_WRITE_THROUGH_READ_AND_WRITE_ALLOCATE   4'b1110
`define AXI_AWCACHE_WRITE_BACK_NO_ALLOCATE                  4'b0111
`define AXI_AWCACHE_WRITE_BACK_READ_ALLOCATE                4'b0111
`define AXI_AWCACHE_WRITE_BACK_WRITE_ALLOCATE               4'b1111
`define AXI_AWCACHE_WRITE_BACK_READ_AND_WRITE_ALLOCATE      4'b1111

`define AXI_SIZE_BYTES_1                                    3'b000
`define AXI_SIZE_BYTES_2                                    3'b001
`define AXI_SIZE_BYTES_4                                    3'b010
`define AXI_SIZE_BYTES_8                                    3'b011
`define AXI_SIZE_BYTES_16                                   3'b100
`define AXI_SIZE_BYTES_32                                   3'b101
`define AXI_SIZE_BYTES_64                                   3'b110
`define AXI_SIZE_BYTES_128                                  3'b111









module	ysyx_210195_axi_rw(
		//global
		input	clock,
		input	reset,
		
		//user	input/output
		input	rw_valid_i,
		output	rw_ready_o,
		input	rw_req_i,				//require read/write
		output	reg	[63:0] data_read_o,
		input	[63:0] data_write_i,
		input	[63:0] rw_addr_i,
		input	[1:0]	rw_size_i,			//传输的字节数
		output	[1:0]	rw_resp_o,
		
		//MASTER write addr
		input	aw_ready_i,			//slave -> master,ready to receive write address
		output	aw_valid_o,			//master -> slave,write address valid
		output	[63:0] aw_addr_o,		//write sddress
		output	[3:0]	aw_id_o,			//write address channel ID
		output	aw_user_o,		//自定义
		output	[2:0]	aw_prot_o,				//access permissions
		output	[7:0]	aw_len_o,			//burst lenth = aw_len + 1
		output	[2:0]	aw_size_o,			//本次burst中，一次transferde的字节数
		output	[1:0]	aw_burst_o,			//burst_type
		output			aw_lock_o,
		output	[3:0]	aw_cache_o,			//memory types
		output	[3:0]	aw_qos_o,			//Quality of service identifier for a write transaction
		output	[3:0]	aw_region_o,		//多接口时用

		//master write data
		input	w_ready_i,
		output	w_valid_o,
		output	reg [63:0]	w_data_o,
		output	reg [7:0]	w_strb_o,				//标志有效位
		output	w_last_o,						//标志最后一次传输
		output	w_user_o,
		
		//write response
		output	b_ready_o,
		input	b_valid_i,
		input	[1:0]	b_resp_i,
		input	[3:0]	b_id_i,
		input	b_user_i,

		//read address channel
		input	ar_ready_i,
		output	ar_valid_o,
		output	[63:0]	ar_addr_o,
		output	[2:0]	ar_prot_o,
		output	[3:0]	ar_id_o,			//read address channel identifier
		output	ar_user_o,
		output	[7:0]	ar_len_o,
		output	[2:0]	ar_size_o,
		output	[1:0]	ar_burst_o,
		output			ar_lock_o,
		output	[3:0]	ar_cache_o,
		output	[3:0]	ar_qos_o,
		output	[3:0]	ar_region_o,		

		//read data channel
		output	r_ready_o,
		input	r_valid_i,
		input	[1:0]	r_resp_i,
		input	[63:0]	r_data_i,
		input	r_last_i,
		input	[3:0]	r_id_i,
		input	r_user_i

);

	wire	w_trans;
	wire	r_trans;
	assign	w_trans = (rw_req_i == `REQ_WRITE);
	assign	r_trans = (rw_req_i == `REQ_READ);
//write or read en
	wire	w_valid;
	wire	r_valid;
	assign	w_valid = rw_valid_i & w_trans;
	assign	r_valid = rw_valid_i & r_trans;
//handshake signal
	wire	aw_hs = aw_valid_o & aw_ready_i;
	wire	w_hs = w_valid_o & w_ready_i;
	wire	b_hs = b_valid_i & b_ready_o;
	
	wire	ar_hs = ar_valid_o & ar_ready_i;
	wire	r_hs = r_valid_i & r_ready_o;
	
	wire	w_done = w_hs & w_last_o;
	wire	r_done = r_hs & r_last_i;
	wire	trans_done = w_trans ? b_hs : r_done;
	
	
//write machine
	parameter	[1:0]	W_STATE_IDLE = 2'b00,
						W_STATE_ADDR = 2'b01,
						W_STATE_WRITE = 2'b10,
						W_STATE_RESP = 2'b11;
	reg	[1:0]	w_state;
	reg	[1:0]	w_state_next;
	reg		rw_ready;
	always @(posedge clock)	begin
		if(reset)
			w_state <= W_STATE_IDLE;
		else
			w_state <= (w_valid & rw_ready == 1'b0) ? w_state_next : w_state;	
	
	end
	
	always @(*)	begin
		case(w_state)
			W_STATE_IDLE:	w_state_next = W_STATE_ADDR;
			W_STATE_ADDR:	w_state_next = aw_hs ? W_STATE_WRITE : W_STATE_ADDR;
			W_STATE_WRITE:	w_state_next = w_done ? W_STATE_RESP : W_STATE_WRITE;
			W_STATE_RESP:	w_state_next = b_hs ? W_STATE_IDLE : W_STATE_RESP;
			default:		w_state_next = W_STATE_IDLE;
		endcase
		
	end
	
	
//read state machine
	parameter	[1:0]	R_STATE_IDLE = 2'b00,
						R_STATE_ADDR = 2'b01,
						R_STATE_READ = 2'b10;
	reg	[1:0]	r_state;
	reg	[1:0]	r_state_next;
	
	always @(posedge clock)	begin
		if(reset)
			r_state <= R_STATE_IDLE;
		else
			r_state <= (r_valid & rw_ready == 1'b0) ? r_state_next : r_state;
	
	end
	always @(*)	begin
		case(r_state)
			R_STATE_IDLE:	r_state_next = R_STATE_ADDR;
			R_STATE_ADDR:	r_state_next = ar_hs ? R_STATE_READ : R_STATE_ADDR;
			R_STATE_READ:	r_state_next = r_done ? R_STATE_IDLE : R_STATE_READ;
			default:		r_state_next = R_STATE_IDLE;
		endcase
	
	end
	wire	bits32_sel;
	assign	bits32_sel = (rw_addr_i < 64'h0000_0000_8000_0000) ? 1'b1 : 1'b0;	
//	assign	bits32_sel = 1'b0;





	
//process data
	wire	aligned;		//对齐传输1'b1
	assign	aligned = bits32_sel ? (rw_addr_i[1:0] == 2'b00) : (rw_addr_i[2:0] == 3'b000);
	wire	size_b;
	wire	size_h;
	wire	size_w;
	wire	size_d;
	assign	size_b = (rw_size_i == `SIZE_B);
	assign	size_h = (rw_size_i == `SIZE_H);
	assign	size_w = (rw_size_i == `SIZE_W);
	assign	size_d = (rw_size_i == `SIZE_D);
	wire	[3:0]	addr_op1;				//取起始后几位地址
	wire	[3:0]	addr_op2;				//地址增量
	wire	[3:0]	addr_end;				//结束地址后四位
	assign	addr_op1 = bits32_sel ? {2'b00,rw_addr_i[1:0]} : {1'b0,rw_addr_i[2:0]};
	assign	addr_op2 = ({4{size_b}} & {4'b0000})		
					| ({4{size_h}} & {4'b0001})
					| ({4{size_w}} & {4'b0011})
					| ({4{size_d}} & {4'b0111});
	assign	addr_end = addr_op1 + addr_op2;
	wire	overstep;
	assign	overstep = ~(bits32_sel ? (addr_end[3:2] == 2'b00) : (addr_end[3] == 1'b0));
	wire	[2:0]	axi_size;
	wire	[7:0]	axi_len;
	assign	axi_size = bits32_sel ? 3'b010 : 3'b011;
	assign	axi_len = {7'd0,overstep};
	
	wire	[63:0]	axi_addr;
	assign	axi_addr = bits32_sel ? {rw_addr_i[63:2],2'b00} : {rw_addr_i[63:3],3'b000};
	
	wire	[3:0]	axi_id = 3'b000;
	wire	axi_user = 1'b0;
//标志有效位	
	reg 	[63:0]	mask_l;
	reg 	[63:0]	mask_h;
	wire	[7:0]	aligned_offset_l;		//第一个相对于对齐地址的偏移，向右偏
	wire	[7:0]	aligned_offset_h;		//第二个相对于对其地址的偏移，向左偏
	assign	aligned_offset_l = bits32_sel ? ({6'd0,rw_addr_i[1:0]} << 3) :
											({5'd0,rw_addr_i[2:0]} << 3);
	assign	aligned_offset_h = bits32_sel ? (32 - aligned_offset_l) : 
											(64 - aligned_offset_l);
	always @(*)	begin
		case(bits32_sel)
			1'b0:	begin		//64bit
				case(rw_addr_i[2:0])
					3'b000:	begin
						mask_l = size_b ? 64'h0000_0000_0000_00ff : 
								size_h ? 64'h0000_0000_0000_ffff :
								size_w ? 64'h0000_0000_ffff_ffff :
								size_d ? 64'hffff_ffff_ffff_ffff : 64'd0;
						mask_h = 64'd0;
					end
					3'b001:	begin
						mask_l = size_b ? 64'h0000_0000_0000_ff00 :
								size_h ? 64'h0000_0000_00ff_ff00 :
								size_w ? 64'h0000_00ff_ffff_ff00 :
								size_d ? 64'hffff_ffff_ffff_ff00 : 64'd0;
						mask_h = size_d ? 64'h0000_0000_0000_00ff : 64'd0;
					
					end
					3'b010:	begin
						mask_l = size_b ? 64'h0000_0000_00ff_0000 : 
								size_h ? 64'h0000_0000_ffff_0000 : 
								size_w ? 64'h0000_ffff_ffff_0000 :
								size_d ? 64'hffff_ffff_ffff_0000 : 64'd0;
						mask_h = size_d ? 64'h0000_0000_0000_ffff : 64'd0;
					
					end
					3'b011:	begin
						mask_l = size_b ? 64'h0000_0000_ff00_0000 :
								size_h ? 64'h0000_00ff_ff00_0000 :
								size_w ? 64'h00ff_ffff_ff00_0000 :
								size_d ? 64'hffff_ffff_ff00_0000 : 64'd0;
						mask_h = size_d ? 64'h0000_0000_00ff_ffff : 64'd0;					
					end
					3'b100:	begin
						mask_l = size_b ? 64'h0000_00ff_0000_0000 :
								size_h ? 64'h0000_ffff_0000_0000 :
								size_w ? 64'hffff_ffff_0000_0000 : 
								size_d ? 64'hffff_ffff_0000_0000 : 64'd0;
						mask_h = size_d ? 64'h0000_0000_ffff_ffff : 64'd0;
					end
					3'b101:	begin
						mask_l = size_b ? 64'h0000_ff00_0000_0000 :
								size_h ? 64'h00ff_ff00_0000_0000 :
								size_w ? 64'hffff_ff00_0000_0000 :
								size_d ? 64'hffff_ff00_0000_0000 : 64'd0;
						mask_h = size_w ? 64'h0000_0000_0000_00ff :
								size_d ? 64'h0000_00ff_ffff_ffff : 64'd0;					
					end
					3'b110:	begin
						mask_l = size_b ? 64'h00ff_0000_0000_0000 :
								size_h ? 64'hffff_0000_0000_0000 :
								size_w ? 64'hffff_0000_0000_0000 :
								size_d ? 64'hffff_0000_0000_0000 : 64'd0;
						mask_h = size_w ? 64'h0000_0000_0000_ffff :
								size_d ? 64'h0000_ffff_ffff_ffff : 64'd0;
					
					end
					3'b111:	begin
						mask_l = size_b ? 64'hff00_0000_0000_0000 :
								size_h ? 64'hff00_0000_0000_0000 :
								size_w ? 64'hff00_0000_0000_0000 :
								size_d ? 64'hff00_0000_0000_0000 : 64'd0;
						mask_h = size_h ? 64'h0000_0000_0000_00ff :
								size_w ? 64'h0000_0000_00ff_ffff :
								size_d ? 64'h00ff_ffff_ffff_ffff : 64'd0;
					end
				endcase
			end
			1'b1:	begin		//32bit
				case(rw_addr_i[1:0])
					2'b00:	begin
						mask_l = size_b ? 64'h0000_0000_0000_00ff :
								size_h ? 64'h0000_0000_0000_ffff :
								size_w ? 64'h0000_0000_ffff_ffff : 64'd0;
						mask_h = 64'd0;
					end
					2'b01:	begin
						mask_l = size_b ? 64'h0000_0000_0000_ff00 :
								size_h ? 64'h0000_0000_00ff_ff00 :
								size_w ? 64'h0000_0000_ffff_ff00 : 64'd0;
						mask_h = size_w ? 64'h0000_0000_0000_00ff : 64'd0;
					
					end
					2'b10:	begin
						mask_l = size_b ? 64'h0000_0000_00ff_0000 :
								size_h ? 64'h0000_0000_ffff_0000 :
								size_w ? 64'h0000_0000_ffff_0000 : 64'd0;
						mask_h = size_w ? 64'h0000_0000_0000_ffff : 64'd0;
					end
					2'b11:	begin
						mask_l = size_b ? 64'h0000_0000_ff00_0000 :
								size_h ? 64'h0000_0000_ff00_0000 :
								size_w ? 64'h0000_0000_ff00_0000 : 64'd0;
						mask_h = size_h ? 64'h0000_0000_0000_00ff :	
								size_w ? 64'h0000_0000_00ff_ffff : 64'd0;
					end
					
				endcase
			end
		endcase
	
	end
	
	reg	[7:0]	len;
	wire	len_reset = reset | (w_trans & (w_state == W_STATE_IDLE)) | (r_trans & (r_state == R_STATE_IDLE));
	wire	len_incr_en = (len != axi_len) & (w_hs | r_hs);	
	always @(posedge clock)
	  begin
		if(len_reset)
			len <= 8'h00;
		else if(len_incr_en)
			len <= len + 1'b1;	  
	  end
	
	wire	uart_addr;
	assign	uart_addr = (rw_addr_i > 64'h0000_0000_0fff_ffff) && (rw_addr_i < 64'h0000_0000_1000_1000);
	  
//write address channel signal
	assign	aw_valid_o = (w_state == W_STATE_ADDR);
	assign	aw_addr_o = uart_addr ? rw_addr_i : axi_addr;
	assign	aw_id_o = axi_id;
	assign	aw_user_o = axi_user;
	assign	aw_prot_o = `AXI_PROT_UNPRIVILEGED_ACCESS | `AXI_PROT_SECURE_ACCESS | `AXI_PROT_DATA_ACCESS;
	assign	aw_len_o = axi_len;
	assign	aw_size_o = axi_size;
	assign	aw_burst_o = `AXI_BURST_TYPE_INCR;
	assign	aw_lock_o = 1'b0;
	assign	aw_cache_o = `AXI_ARCACHE_NORMAL_NON_CACHEABLE_NON_BUFFERABLE;
	assign	aw_qos_o = 4'h0;
	
	
//write data channel
	assign	w_valid_o = (w_state == W_STATE_WRITE);
	wire	[63:0]	axi_w_data_l;
	wire	[63:0]	axi_w_data_h;
	assign	w_last_o = w_hs & (len == axi_len);
	assign	axi_w_data_l = data_write_i << aligned_offset_l;
	assign	axi_w_data_h = data_write_i >> aligned_offset_h;
	wire	[63:0]	w_data_nuart;
	assign	w_data_nuart = (len[0] == 1'b0) ? axi_w_data_l : axi_w_data_h;
	wire	[7:0]	w_strb_nuart;
	assign	w_strb_nuart = (len[0] == 1'b0) ? {mask_l[56],mask_l[48],mask_l[40],mask_l[32],mask_l[24],mask_l[16],mask_l[8],mask_l[0]}
												: {mask_h[56],mask_h[48],mask_h[40],mask_h[32],mask_h[24],mask_h[16],mask_h[8],mask_h[0]};
//	wire	[7:0]	w_strb_uart;
//	assign	w_strb_uart = ({8{size_b}} & {8'b0000_0001})		
//					| ({8{size_h}} & {8'b0000_0011})
//					| ({8{size_w}} & {8'b0000_1111})
//					| ({8{size_d}} & {8'b1111_1111});

		always @(negedge clock) begin
			if(reset)	begin
				w_data_o <= 64'd0;
				w_strb_o <= 8'h00;
			end
			else if(w_ready_i & w_valid_o)	begin
				if(~aligned & overstep)	begin
					w_data_o <= w_data_nuart;
					w_strb_o <= w_strb_nuart;
				end
				else if(axi_len == 8'h00)	begin
					w_data_o <= axi_w_data_l;
					w_strb_o <= {mask_l[56],mask_l[48],mask_l[40],mask_l[32],mask_l[24],mask_l[16],mask_l[8],mask_l[0]};
				
				end
			end
		end


//write response channel
	assign	b_ready_o = (w_state == W_STATE_RESP);
	
//read address channel 
	assign	ar_valid_o = (r_state == R_STATE_ADDR);
	assign	ar_addr_o = uart_addr ? rw_addr_i : axi_addr;
	assign	ar_prot_o = `AXI_PROT_UNPRIVILEGED_ACCESS | `AXI_PROT_SECURE_ACCESS | `AXI_PROT_DATA_ACCESS;
	assign	ar_id_o = axi_id;
	assign	ar_user_o = axi_user;
	assign	ar_len_o = axi_len;
	assign	ar_size_o = axi_size;
	assign	ar_burst_o = `AXI_BURST_TYPE_INCR;
	assign	ar_lock_o = 1'b0;
	assign	ar_cache_o = `AXI_ARCACHE_NORMAL_NON_CACHEABLE_NON_BUFFERABLE;	
	
//read data channel
	assign	r_ready_o = (r_state == R_STATE_READ);
	wire	[63:0]	axi_r_data_l;
	wire	[63:0]	axi_r_data_h;
	assign	axi_r_data_l = (r_data_i & mask_l) >> aligned_offset_l;
	assign	axi_r_data_h  = (r_data_i & mask_h) << aligned_offset_h;
//	wire	[63:0]	data_read_nuart;
//	assign	data_read_nuart = (len[0] == 1'b0) ? axi_r_data_l : 
//													(data_read_o[63:0] | axi_r_data_h);

		always @(posedge clock)	begin
			if(reset)	begin
				data_read_o[63:0] <= 0;
			end
			else if(r_ready_o & r_valid_i)	begin
				if(~aligned & overstep)	begin
					data_read_o <= (len[0] == 1'b0) ? axi_r_data_l : 
													(data_read_o[63:0] | axi_r_data_h);
				end
				else if(axi_len == 8'h00)	begin
					data_read_o <= axi_r_data_l;
				
				end
			end
		end


//rw_ready_o;rw_resp_o

	assign	rw_ready_o = rw_ready;
always @(posedge clock)
  begin
	if(reset)	begin
		rw_ready <= 1'b0;
	end
	else if(trans_done | rw_ready)	begin
		rw_ready <= trans_done;
	end
  end
  
	reg		[1:0]	rw_resp;
	assign	rw_resp_o = rw_resp;
always @(posedge clock)
  begin
	if(reset)	begin
		rw_resp <= 2'b00;
	end
	else if(trans_done)	begin
		rw_resp <= w_trans ? b_resp_i : r_resp_i;
	end
  
  end

endmodule
	